use std::ffi::{CStr, CString};
use std::ptr::null_mut;
use chrono::{DateTime, Utc};
use oh_napi_sys::*;
use paste::paste;
use crate::error::*;
macro_rules! fn_get_value_base {
    ($dst: ident, $rust: ident) => {
        paste!{
            pub unsafe fn [ < get_ $dst _from >](&self, value: napi_value)-> Result<$rust>{
                let mut out = $rust::default();
                    let state = [<napi_get_value_ $dst>] (self.0, value, &mut out);
                    self.check_status(state)?;
                Ok(out)
            }
        }
    }
}

macro_rules! fn_create_base {
    ($dst: ident, $rust: ident) => {

        paste!{
             pub unsafe fn [ < create_ $dst > ](&self, value: $rust)->Result<napi_value>{
                let mut out: napi_value = null_mut();
                    self.check_status([< napi_create_ $dst >](self.0, value, &mut out))?;
                Ok(out)
            }

        }
    };
}



pub struct Env(napi_env);

impl From<napi_env> for Env {
    fn from(value: napi_env) -> Self {
        Self(value)
    }
}

pub trait  GetValue<T>{
    unsafe fn get_value(&self, value: napi_value)->Result<T>;
}

impl Env {
    pub fn check_status(&self, result: napi_status) -> Result<()> {
        if result == napi_status_napi_ok {
            return Ok(());
        }
        Err(NAPIError { status: Status::from(result), msg: "".into() })
    }

    /// # Safety
    ///
    /// 用于获取函数传入参数。
    pub unsafe fn get_args(&self, info: napi_callback_info) -> Result<Vec<napi_value>> {
        let mut argc = 0;
        self.check_status(napi_get_cb_info(self.0, info, &mut argc, null_mut(), null_mut(), null_mut()))?;
        let mut args: Vec<napi_value> = vec![null_mut(); argc as usize];
        self.check_status(napi_get_cb_info(self.0, info, &mut argc, args.as_mut_ptr(), null_mut(), null_mut()))?;
        Ok(args)
    }
    pub fn throw_error(&self, code: &str, msg: &str) {
        unsafe {
            let code = CString::new(code).unwrap();
            let msg = CString::new(msg).unwrap();
            napi_throw_error(self.0, code.as_ptr(), msg.as_ptr());
        }
    }
    pub fn throw_this_error(&self, error: NAPIError) {
        self.throw_error(error.status.as_ref(), error.msg.as_str())
    }

    fn_get_value_base!(double, f64);
    fn_get_value_base!(int32, i32);
    fn_get_value_base!(uint32, u32);
    fn_get_value_base!(int64, i64);
    fn_get_value_base!(bool, bool);

    pub unsafe fn get_value_string_utf8(&self, value: napi_value) -> Result<String> {
        let out = String::new();
        let mut length = 0;
        self.check_status(napi_get_value_string_utf8(self.0, value, null_mut(), 0, &mut length))?;
        let mut buf = vec![0; (length + 1) as usize];
        self.check_status(napi_get_value_string_utf8(self.0, value, buf.as_mut_ptr(), buf.len() as _, &mut length))?;
        let str = CStr::from_ptr(buf.as_ptr());
        Ok(str.to_string_lossy().to_string())
    }

    pub unsafe fn get_value_date(&self, value: napi_value) -> Result<DateTime<Utc>> {
        let mut ts_ms = 0.;
        self.check_status(napi_get_date_value(self.0, value, &mut ts_ms))?;
        let sec = (ts_ms / 1000.0) as i64;
        let left = ts_ms - sec as f64 * 1000.0;
        DateTime::from_timestamp(sec, (left * 1000.0) as u32)
            .ok_or(NAPIError::new(format!("DateTime[{}] parse error", ts_ms)))
    }

    pub unsafe fn get_array_length(&self, value: napi_value)->Result<usize>{
        let mut len = 0;
        self.check_status(napi_get_array_length(self.0, value, &mut len))?;
        Ok(len as _)
    }

    // pub unsafe fn get_value_slice<T>(&self, value: napi_value)->Result<Slice<T>>{
    //     let mut len = 0;
    //     self.check_status(napi_get_array_length(self.0, value, &mut len))?;
    //     let slice = Slice::new(self, value, len as _);
    //     Ok(slice)
    // }

    fn_create_base!(double, f64);
    fn_create_base!(int32, i32);
    fn_create_base!(uint32, u32);
    fn_create_base!(int64, i64);

    pub unsafe fn create_string_utf8(&self, value: String) -> Result<napi_value> {
        let mut out: napi_value = null_mut();
        let cstr = CString::new(value).unwrap();
        self.check_status(napi_create_string_utf8(self.0, cstr.as_ptr(), NAPI_AUTO_LENGTH as _, &mut out))?;
        Ok(out)
    }

    pub unsafe fn create_date(&self, date: &DateTime<Utc>) -> Result<napi_value> {
        let ts_ms = date.timestamp_millis() as f64;
        let mut out = null_mut();
        self.check_status(napi_create_date(self.0, ts_ms, &mut out))?;

        Ok(out)
    }

    pub unsafe fn create_array(&self, data: &[napi_value])->Result<napi_value>{
        let mut arr = null_mut();
        self.check_status(
        napi_create_array_with_length(self.0, data.len() as _, &mut arr))?;

        for (i, value) in data.iter().copied().enumerate(){
            self.check_status(
            napi_set_element(self.0, arr, i as _ , value))?;
        }

        Ok(arr)
    }

    pub unsafe fn get_element(&self, arr: napi_value, index: usize)->Result<napi_value>{
        let mut v = null_mut();
        self.check_status(napi_get_element(self.0, arr, index as _, &mut v))?;
        Ok(v)
    }

}

impl GetValue<i32> for Env{
    unsafe  fn get_value(&self, value: napi_value) -> Result<i32> {
        self.get_int32_from(value)
    }
}
impl GetValue<u32> for Env{
    unsafe  fn get_value(&self, value: napi_value) -> Result<u32> {
        self.get_uint32_from(value)
    }
}
